// SPDX-FileCopyrightText: Copyright 2025-2025 深圳市同心圆网络有限公司 
// SPDX-License-Identifier: GPL-3.0-only 

import type { BoardInfo } from "$lib/api/board/board";
import type { DrawEdgeInfo, DrawInfo, EdgeKey, ExDrawNodeInfo } from "$lib/api/board/board_draw";
import { get_draw, get_edge, get_node, list_edge, list_node } from "$lib/api/board/board_draw";
import type { NodeContent } from "$lib/api/board/board_draw_type";
import type { MemberInfo } from "$lib/api/board/board_member";
import {
    list as list_member,
    get as get_member
} from "$lib/api/board/board_member";
import { get_user_session } from "$lib/api/user";
import { request } from "$lib/utils/request";
import type { board as board_notice } from '$lib/api/notice_type';
import dayjs from "dayjs";
import { list as list_extdata, type ExtDataInfo } from "$lib/api/board/board_extdata";

export interface DrawTool {
    content: NodeContent;
    defaultWidth: number;
    defaultHeight: number;
    showModal: boolean;
    fromNodeId?: string;
}

export type ToolInfo = "move" | DrawTool;


export interface UserPosition {
    memberUserId: string;
    x: number;
    y: number;
    timeStamp: number;
}


class DrawStore {
    private _boardInfo: null | BoardInfo = $state(null);
    private _myMember: null | MemberInfo = $state(null);
    private _drawId = $state("");
    private _drawInfo: null | DrawInfo = $state(null);
    private _selectNodeIdList = $state([] as string[]);
    private _selectEdgeKeyList = $state([] as EdgeKey[]);
    private _boardMemberList = $state([] as MemberInfo[]);

    public nodeList = $state([] as ExDrawNodeInfo[]);
    public edgeList = $state([] as DrawEdgeInfo[]);
    public userPositionList = $state([] as UserPosition[]);

    public extDataInfoList = $state([] as ExtDataInfo[]);

    public curToolInfo: ToolInfo = $state("move");

    public inConnect = $state(false);
    public skipUpdateNode = false;

    public hoverNodeId = $state("");

    get boardInfo() {
        return this._boardInfo;
    }

    get myMember() {
        return this._myMember;
    }

    get boardMemberList() {
        return this._boardMemberList;
    }

    get drawId() {
        return this._drawId;
    }

    get drawInfo() {
        return this._drawInfo;
    }

    get selectNodeIdList() {
        return this._selectNodeIdList;
    }

    get selectEdgeKeyList() {
        return this._selectEdgeKeyList;
    }

    public readOnly = $derived(this._myMember?.admin == false && (this._drawInfo?.edit_member_user_id_list ?? []).includes(this._myMember.member_user_id) == false);


    async init( boardInfo: BoardInfo, myMember: MemberInfo, drawId: string) {

        if (this._drawId == drawId) {
            this._boardInfo = boardInfo;
            this._myMember = myMember;
            return;
        }
        let needReloadMember = false;
        if (this._boardInfo?.board_id != boardInfo.board_id) {
            needReloadMember = true;
        }
        this._boardInfo = boardInfo;
        this._myMember = myMember;
        this._drawId = drawId;
        this._selectNodeIdList = [];
        this._selectEdgeKeyList = [];

        if (needReloadMember) {
            await this.loadMemberList();
        }
        await this.loadDrawInfo();
        await this.loadExtDataInfoList();
        //加载所有节点
        await this.loadNodeList();
        //加载所有连线 
        await this.loadEdgeList();
    }

    reset() {
        this._drawId = "";
    }

    async loadMemberList() {
        const sessionId = await get_user_session();
        const res = await request(list_member({
            session_id: sessionId,
            board_id: this._boardInfo?.board_id ?? "",
        }));
        this._boardMemberList = res.member_list;
    }

    private async loadDrawInfo() {
        const sessionId = await get_user_session();
        const res = await request(get_draw({
            session_id: sessionId,
            board_id: this._boardInfo?.board_id ?? "",
            draw_id: this._drawId,
        }));
        this._drawInfo = res.draw;
    }

    private async loadExtDataInfoList() {
        const sessionId = await get_user_session();
        const res = await request(list_extdata({
            session_id: sessionId,
            board_id: this.boardInfo?.board_id ?? "",
        }));
        this.extDataInfoList = res.ext_data_list;
    }

    async loadNodeList() {
        const sessionId = await get_user_session();
        const res = await request(list_node({
            session_id: sessionId,
            board_id: this._boardInfo?.board_id ?? "",
            draw_id: this._drawId,
        }));
        this.nodeList = res.node_list;
    }

    async loadEdgeList() {
        const sessionId = await get_user_session();
        const res = await request(list_edge({
            session_id: sessionId,
            board_id: this._boardInfo?.board_id ?? "",
            draw_id: this._drawId,
        }));
        this.edgeList = res.edge_list;
    }

    addSelectNodeId(nodeId: string) {
        const index = this.nodeList.findIndex(item => item.node_id == nodeId);
        if (index == -1) {
            return;
        }
        const tmpList = this._selectNodeIdList.slice();
        if (tmpList.includes(nodeId)) {
            return;
        }
        tmpList.push(nodeId);
        this._selectNodeIdList = tmpList;
    }

    removeSelectNodeId(nodeId: string) {
        if (this._selectNodeIdList.includes(nodeId)) {
            const tmpList = this._selectNodeIdList.filter(item => item != nodeId);
            this._selectNodeIdList = tmpList;
        }
    }

    addSelectEdgeKey(edgeKey: EdgeKey) {
        const index = this.edgeList.findIndex(item => (
            item.edge_key.from_node_id == edgeKey.from_node_id
            && item.edge_key.from_handle == edgeKey.from_handle
            && item.edge_key.to_node_id == edgeKey.to_node_id
            && item.edge_key.to_handle == edgeKey.to_handle
        ));
        if (index == -1) {
            return;
        }
        const tmpList = this._selectEdgeKeyList.slice();
        if (tmpList.find(item => (
            item.from_node_id == edgeKey.from_node_id
            && item.from_handle == edgeKey.from_handle
            && item.to_node_id == edgeKey.to_node_id
            && item.to_handle == edgeKey.to_handle
        )) != undefined) {
            return;
        }
        tmpList.push(edgeKey);
        this._selectEdgeKeyList = tmpList;
    }

    removeSelectEdgeKey(edgeKey: EdgeKey) {
        const tmpList = this._selectEdgeKeyList.filter(item => !(
            item.from_node_id == edgeKey.from_node_id
            && item.from_handle == edgeKey.from_handle
            && item.to_node_id == edgeKey.to_node_id
            && item.to_handle == edgeKey.to_handle
        ));
        if (tmpList.length != this._selectEdgeKeyList.length) {
            this._selectEdgeKeyList = tmpList;
        }
    }

    async onCreateNode(nodeId: string) {
        const tmpList = this.nodeList.slice();
        const index = tmpList.findIndex(item => item.node_id == nodeId);
        if (index != -1) {
            return;
        }
        const sessionId = await get_user_session();
        const res = await request(get_node({
            session_id: sessionId,
            board_id: this._boardInfo?.board_id ?? "",
            draw_id: this._drawId,
            node_id: nodeId,
        }));
        tmpList.push(res.node);
        this.nodeList = tmpList;
    }

    public async onUpdateNode(nodeId: string) {
        if (this.skipUpdateNode) {
            return;
        }
        const tmpList = this.nodeList.slice();
        const index = tmpList.findIndex(item => item.node_id == nodeId);
        if (index == -1) {
            return;
        }
        const sessionId = await get_user_session();
        const res = await request(get_node({
            session_id: sessionId,
            board_id: this._boardInfo?.board_id ?? "",
            draw_id: this._drawId,
            node_id: nodeId,
        }));
        tmpList[index] = res.node;
        this.nodeList = tmpList;
    }

    onRemoveNode(nodeId: string) {
        //移除相关链接
        const tmpEdgeList = this.edgeList.filter(item => !(item.edge_key.from_node_id == nodeId || item.edge_key.to_node_id == nodeId));
        this.edgeList = tmpEdgeList;
        //移除相关节点
        const tmpNodeList = this.nodeList.filter(item => item.node_id != nodeId);
        this.nodeList = tmpNodeList;
        this.removeSelectNodeId(nodeId);
    }

    async onJoinBoard(memberUserId: string) {
        const tmpList = this._boardMemberList.slice();
        const index = tmpList.findIndex(item => item.member_user_id == memberUserId);
        if (index != -1) {
            return;
        }
        const sessionId = await get_user_session();
        const res = await request(get_member({
            session_id: sessionId,
            board_id: this._boardInfo?.board_id ?? "",
            member_user_id: memberUserId,
        }));
        tmpList.push(res.member);
        this._boardMemberList = tmpList;
    }

    async onUpdateMember(memberUserId: string) {
        const tmpList = this._boardMemberList.slice();
        const index = tmpList.findIndex(item => item.member_user_id == memberUserId);
        if (index == -1) {
            return;
        }
        const sessionId = await get_user_session();
        const res = await request(get_member({
            session_id: sessionId,
            board_id: this._boardInfo?.board_id ?? "",
            member_user_id: memberUserId,
        }));
        tmpList[index] = res.member;
        this._boardMemberList = tmpList;
    }

    onLeaveBoard(memberUserId: string) {
        const tmpList = this._boardMemberList.filter(item => item.member_user_id != memberUserId);
        this._boardMemberList = tmpList;
    }

    async onCreateEdge(edgeKey: EdgeKey) {
        const tmpList = this.edgeList.slice();
        const index = tmpList.findIndex(item =>
            item.edge_key.from_node_id == edgeKey.from_node_id
            && item.edge_key.from_handle == edgeKey.from_handle
            && item.edge_key.to_node_id == edgeKey.to_node_id
            && item.edge_key.to_handle == edgeKey.to_handle);
        if (index != -1) {
            return;
        }
        const sessionId = await get_user_session();
        const res = await request(get_edge({
            session_id: sessionId,
            board_id: this._boardInfo?.board_id ?? "",
            draw_id: this._drawId,
            edge_key: edgeKey,
        }));
        tmpList.push(res.edge);
        this.edgeList = tmpList;
    }

    async onUpdateEdge(edgeKey: EdgeKey) {
        const tmpList = this.edgeList.slice();
        const index = tmpList.findIndex(item =>
            item.edge_key.from_node_id == edgeKey.from_node_id
            && item.edge_key.from_handle == edgeKey.from_handle
            && item.edge_key.to_node_id == edgeKey.to_node_id
            && item.edge_key.to_handle == edgeKey.to_handle);
        if (index == -1) {
            return;
        }
        const sessionId = await get_user_session();
        const res = await request(get_edge({
            session_id: sessionId,
            board_id: this._boardInfo?.board_id ?? "",
            draw_id: this._drawId,
            edge_key: edgeKey,
        }));
        tmpList[index] = res.edge;
        this.edgeList = tmpList;
    }

    onRemoveEdge(edgeKey: EdgeKey) {
        const tmpList = this.edgeList.filter(item => !(
            item.edge_key.from_node_id == edgeKey.from_node_id
            && item.edge_key.from_handle == edgeKey.from_handle
            && item.edge_key.to_node_id == edgeKey.to_node_id
            && item.edge_key.to_handle == edgeKey.to_handle
        ));
        this.edgeList = tmpList;
    }

    onRemoveEdgeByHandle(nodeId: string, handleList: string[]) {
        const tmpList = this.edgeList.filter(item => !(
            (item.edge_key.from_node_id == nodeId && handleList.includes(item.edge_key.from_handle))
            ||
            (item.edge_key.to_node_id == nodeId && handleList.includes(item.edge_key.to_handle))
        ));
        this.edgeList = tmpList;
    }

    updateUserPositionList(memberUserId: string, x: number, y: number) {
        if (memberUserId == this.myMember?.member_user_id) {
            return;
        }
        const nowTime = dayjs().valueOf();
        const tmpList = this.userPositionList.filter(item => (nowTime - item.timeStamp < 60 * 1000));
        const index = tmpList.findIndex(item => item.memberUserId == memberUserId);
        if (index == -1) {
            tmpList.push({
                memberUserId: memberUserId,
                x: x,
                y: y,
                timeStamp: nowTime,
            })
        } else {
            tmpList[index].x = x;
            tmpList[index].y = y;
            tmpList[index].timeStamp = nowTime;
        }
        this.userPositionList = tmpList;
    }

    async processBoardNotice(notice: board_notice.AllNotice) {
        if (notice.UpdateDrawNotice != undefined && notice.UpdateDrawNotice.draw_id == this._drawId) {
            await this.loadDrawInfo();
        } else if (notice.JoinBoardNotice != undefined && notice.JoinBoardNotice.board_id == this._boardInfo?.board_id) {
            await this.onJoinBoard(notice.JoinBoardNotice.member_user_id);
        } else if (notice.LeaveBoardNotice != undefined && notice.LeaveBoardNotice.board_id == this._boardInfo?.board_id) {
            this.onLeaveBoard(notice.LeaveBoardNotice.member_user_id);
        } else if (notice.UpdateBoardMemberPermNotice != undefined && notice.UpdateBoardMemberPermNotice.board_id != this._boardInfo?.board_id) {
            await this.onUpdateMember(notice.UpdateBoardMemberPermNotice.member_user_id);
        } else if (notice.CreateNodeNotice != undefined && notice.CreateNodeNotice.draw_id == this._drawId) {
            await this.onCreateNode(notice.CreateNodeNotice.node_id);
        } else if (notice.UpdateNodeNotice != undefined && notice.UpdateNodeNotice.draw_id == this._drawId) {
            await this.onUpdateNode(notice.UpdateNodeNotice.node_id);
        } else if (notice.RemoveNodeNotice != undefined && notice.RemoveNodeNotice.draw_id == this._drawId) {
            this.onRemoveNode(notice.RemoveNodeNotice.node_id);
        } else if (notice.CreateEdgeNotice != undefined && notice.CreateEdgeNotice.draw_id == this._drawId) {
            await this.onCreateEdge({
                from_node_id: notice.CreateEdgeNotice.from_node_id,
                from_handle: notice.CreateEdgeNotice.from_handle,
                to_node_id: notice.CreateEdgeNotice.to_node_id,
                to_handle: notice.CreateEdgeNotice.to_handle,
            });
        } else if (notice.UpdateEdgeNotice != undefined && notice.UpdateEdgeNotice.draw_id == this._drawId) {
            await this.onUpdateEdge({
                from_node_id: notice.UpdateEdgeNotice.from_node_id,
                from_handle: notice.UpdateEdgeNotice.from_handle,
                to_node_id: notice.UpdateEdgeNotice.to_node_id,
                to_handle: notice.UpdateEdgeNotice.to_handle,
            });
        } else if (notice.RemoveEdgeNotice != undefined && notice.RemoveEdgeNotice.draw_id == this._drawId) {
            this.onRemoveEdge({
                from_node_id: notice.RemoveEdgeNotice.from_node_id,
                from_handle: notice.RemoveEdgeNotice.from_handle,
                to_node_id: notice.RemoveEdgeNotice.to_node_id,
                to_handle: notice.RemoveEdgeNotice.to_handle,
            });
        } else if (notice.RemoveEdgeByHandleNotice != undefined && notice.RemoveEdgeByHandleNotice.draw_id == this._drawId) {
            this.onRemoveEdgeByHandle(notice.RemoveEdgeByHandleNotice.node_id, notice.RemoveEdgeByHandleNotice.handle_list);
        } else if (notice.UserMouseChangeNotice != undefined && notice.UserMouseChangeNotice.board_id == this._boardInfo?.board_id && notice.UserMouseChangeNotice.draw_id == this._drawId) {
            this.updateUserPositionList(notice.UserMouseChangeNotice.member_user_id, notice.UserMouseChangeNotice.x, notice.UserMouseChangeNotice.y);
        } else if (notice.ExtDataConfigChangeNotice != undefined && notice.ExtDataConfigChangeNotice.board_id == this._boardInfo?.board_id) {
            await this.loadExtDataInfoList();
        }
    }
}

export const drawStore = $state(new DrawStore());